from . import passpord_bp
from flask import request, current_app, abort, make_response, jsonify, session
from info.utitls.captcha.captcha import captcha
from info import redis_store, constants
from info.utitls.response_code import RET
import re
from info.lib.yuntongxun.sms import CCP
from info.models import User
from datetime import datetime
from info import db


@passpord_bp.route('/login_out', methods=['post'])
def login_out():
    """  退出登陆   """
    # 删除session中的数据即可
    session.pop('user_id', None)
    session.pop('mobile', None)
    session.pop('nick_name', None)
    session.pop('is_admin', None)
    return jsonify(errno=RET.OK, errmsg='退出登陆成功')


@passpord_bp.route('/login', methods=['post'])
def login():
    """登录后端接口"""
    """
    1. 获取参数
        1.1 mobile：手机号码  password:密码
    2. 参数校验
        2.1 非空判断
        2.2 手机格式校验
    3. 业务逻辑
        3.0 根据mobile查询当前用户是否存在
        存在：进行密码对比
        不存在： 提示注册
        3.1 password未加密的密码再次加密后进行比对
        不相等： 提示密码填写错误
        相等：记录用户登录信息，修改用户最后一次登录时间
    4. 返回登录成功
    """
    """1.1 mobile：手机号码  password:密码"""
    param_dict = request.json
    mobile = param_dict.get('mobile')
    password = param_dict.get('password')
    """2. 参数校验
        2.1 非空判断
        2.2 手机格式校验
    """
    if not all([mobile, password]):
        current_app.logger.error('参数不全')
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')
    if not re.match('1[35789][0-9]{9}', mobile):
        current_app.logger.error('手机格式错误')
        return jsonify(errno=RET.PARAMERR, errmsg='手机格式错误')
    """
    3. 业务逻辑
        3.0 根据mobile查询当前用户是否存在
        存在：进行密码对比
        不存在： 提示注册
        3.1 password未加密的密码再次加密后进行比对
        不相等： 提示密码填写错误
        相等：记录用户登录信息，修改用户最后一次登录时间
    """
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询用户对象异常')
    if not user:
        return jsonify(errno=RET.NODATA, errmsg='用户不存在')
    #   password未加密的密码再次加密后进行比对
    if not user.check_password(password):
        # print(user.check_password(password))
        # 不相等，提示密码错误
        return jsonify(errno=RET.DATAERR, errmsg='密码填写错误')
    # 相等 ：记录用户登陆信息（作用：删除登陆信息，就相当于退出登陆），修改最后一次登陆时间
    session["user_id"] = user.id
    session["mobile"] = mobile
    session['nick_name'] = mobile

    # 修改最后一次登陆时间
    user.last_login = datetime.now()
    # 提交修改的操作到数据库
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户数据异常")
    # 4.返回值
    return jsonify(errno=RET.OK, errmsg='登陆成功')


@passpord_bp.route('/register', methods=["post"])
def register():
    """注册后端接口"""
    """
    1. 获取参数（json格式）
        1.1 mobile：手机号码， sms_code:短信验证码， password:密码
    2. 参数校验
       # 2.1 非空判断
       # 2.2 手机号码格式校验
    3. 逻辑处理
        3.1 根据key：SMS_18520340803去redis中获取真实的短信验证码
            有值：从redis数据库删除
            没有值：短信验证码过期了
        3.2 拿用户填写的短信验证码和真实的短信验证码对比
            不相等：提示前端
        3.3 相等： 创建用户对象，并给对应属性赋值，保存到数据库
        3.4 注册成功一般表示登录成功，使用session保存用户基本信息
    4. 返回值
        4.1 返回注册成功
    """
    # 1.1 mobile：手机号码， sms_code:短信验证码， password:密码
    param_dict = request.json
    mobile = param_dict.get('mobile')
    sms_code = param_dict.get("sms_code")
    password = param_dict.get('password')
    # 参数校验
    if not all([mobile, sms_code, password]):
        current_app.logger.error('参数不足')
        return jsonify(errno=RET.PARAMERR, errmsg='参数不足')
    if not re.match('1[35789][0-9]{9}', mobile):
        current_app.logger.error('手机格式错误')
        return jsonify(errno=RET.DATAERR, errmsg='手机格式错误')
    """
    根据key：SMS_18520340803去redis中获取真实的短信验证码
            有值：从redis数据库删除
            没有值：短信验证码过期了
    """
    try:
        real_sms_code = redis_store.get('SMS_%s' % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询短信验证码错误')
    if real_sms_code:
        redis_store.delete('SMS_%s' % mobile)
    else:
        current_app.logger.error('短信验证码过期了')
        return jsonify(errno=RET.NODATA, errmsg="短信验证码过期了")
    """
    3.2 拿用户填写的短信验证码和真实的短信验证码对比
            不相等：提示前端
    """
    if real_sms_code != sms_code:
        # 提示用户再次填写
        current_app.logger.error('短信验证码填写错误')
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码填写错误")

    # 3.3 相等： 创建用户对象，并给对应属性赋值，保存到数据库
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # TODO: 密码还未进行加密
    # 对密码进行加密可以封装一个加密方法  只需要调用这个方法就可以进行加密

    # user.set_password_hash(password)  # 方法1
    # 这个方法是直接给属性赋值，然后用property装饰器重写属性get&set方法
    user.password = password  # 方法2：使用set&get方法进行密码加密处理
    # 记录最后一次的登陆时间
    user.last_login = datetime.now()
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户对象异常")
    """ 3.4 注册成功一般表示登录成功，使用session保存用户基本信息 """
    session['user_id'] = user.id
    session['mobile'] = mobile
    session['nick_name'] = user.nick_name

    # 告诉用户注册成功
    return jsonify(errno=RET.OK, errmsg='注册成功')


# 使用蓝图
# 127.0.0.1:5000/passport/image_code?code_id=UUID
@passpord_bp.route('/image_code')
def get_image_code():
    """获取验证码图片"""

    # 1.获取参数 code_id / UUID  code_id= imageCodeId
    code_id = request.args.get('code_id')

    # 2.校验参数  判断code_id是否有值
    if not code_id:
        current_app.logger.error('参数不足')
        abort(403)
    # 3.业务逻辑处理  1. 生成图片验证码图片&生成验证码真实值 2. 将code_id作为key,把key和生成的值存入redis数据库
    #  并且设置有效时长（后续接口需要校验）
    image_name, image_code, image_data = captcha.generate_captcha()
    try:
        redis_store.setex('ImageCode_%s' % code_id, constants.IMAGE_CODE_REDIS_EXPIRES, image_code)
    except Exception as e:
        current_app.logger.error(e)
        abort(500)
    # 4.返回值  返回验证码图片给前段   image_data 是二进制图片 不能兼容所有浏览器
    response = make_response(image_data)
    # 转换图片的格式
    response.headers["Content-Type"] = 'image/JPEG'
    return response


# 127.0.0.1/passport/sms_code
@passpord_bp.route('/sms_code', methods=['post'])
def send_smscode():
    """发送短信验证码的后端实现"""
    """
    # 获取参数 1. mobile  手机号2. 用户填写的图片验证码值， 3.用户 携带的imageCodeId:UUID

    # 校验参数
    # 1. 非空判断
    # 2.使用正则校验手机号码，

    # 业务逻辑处理
        3.1
        根据用户携带的UUID编码去数据库查找正确的图片值
        如果有值，从数据库删除值（防止多次使用值进行校验，爬虫等）
        如果没有值  验证码过期了
        3.2 进行校验，
            3.2.1  相等 发送短信验证码
            3.2.2 不相等  告诉前段 验证码填写错误，（前段再次生成一张图片）
            TODO 查询手机号码是否注册过
        3.3 发送短信验证码的细节
            3.3.1 生成六位随机短信值
            3.3.2 调用封装好的ccp短信验证码工具工具类发送
            3.3.3 发送短信验证码成功后  保存短信验证码到redis数据库



    # 4 返回值
        4.1 告诉前段发送短信验证码成功
    """
    # 获取参数 1. mobile  手机号2. 用户填写的图片验证码值， 3.用户 携带的imageCodeId:UUID
    param_dict = request.json
    mobile = param_dict.get('mobile')
    image_code = param_dict.get('image_code')
    image_code_id = param_dict.get('image_code_id')
    # 校验参数
    # 1. 非空判断
    if not all([mobile, image_code, image_code_id]):
        current_app.logger.error('参数错误')
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')
    # 校验手机号
    if not re.match('1[3578][0-9]{9}', mobile):
        current_app.logger.error('手机号格式错误')
        return jsonify(errno=RET.PARAMERR, errmsg='手机格式错误')

    # 3.1根据用户携带的UUID编码去数据库查找正确的图片值
    try:
        real_image_code = redis_store.get('ImageCode_%s' % image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询数据库异常')
    if real_image_code:
        try:
            redis_store.delete('ImageCode_%s' % image_code_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='删除数据库值异常')
        #  如果没有值  验证码过期了
    else:
        current_app.logger.error('图片验证码过期了')
        return jsonify(errno=RET.NODATA, errmsg='图片验证码过期了')
    # 3.2 进行校验，
    """
    细节： 忽略大小写 redis 数据库拿出来的数据是二进制  需要在redis数据库定义那里转码decode_responses = True
    """
    if image_code.lower() != real_image_code.lower():
        current_app.logger.error('填写图片验证码错误')
        return jsonify(errno=RET.DATAERR, errmsg='填写图片验证码错误')

    # TODO
    # 查询手机号码是否注册过
    try:
        from info.models import User
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询数据异常')
    if user:
        # 用户已经注册
        return jsonify(errno=RET.DATAEXIST, errmsg='用户已经注册')

    # 发送短信验证细节 生成六位随机值
    import random
    sms_code = random.randint(0, 999999)
    # 不足六位补足六位
    sms_code = '%06d' % sms_code
    # 调用封装号的ccp短信验证码工具发送
    try:
        result = CCP().send_template_sms(mobile, {sms_code, constants.SMS_CODE_REDIS_EXPIRES/60}, 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='短信验证码发送失败')
    if result == -1:
        current_app.logger.error('短信验证码发送失败')
        return jsonify(errno=RET.THIRDERR, errmsg='短信验证码发送失败')

    # 发送短信验证码成功后
    # 保存短信验证码到redis数据库
    try:
        redis_store.setex('SMS_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='保存短信失败')
    # 4. 返回值
    return jsonify(errno=RET.OK, errmsg='短信验证码发送成功')







